#pragma once
#include "LXData.h"
#include <string>
namespace LX
{
    struct LXMysqlData;
    class LXAPI LXMysql
    {
        using uint64_t = unsigned long long;

    public:
        enum class EXEC_TYPE : bool
        {
            EXEC_TYPE_USE = 0,
            EXEC_TYPE_STORE = 1
        };
        LXMysql();
        ~LXMysql();
        /**
         * @brief 初始化MySQL连接对象
         *
         * 本函数负责初始化MySQL连接对象，执行以下操作：
         * 1. 检查内部数据容器有效性
         * 2. 关闭已有数据库连接
         * 3. 创建新的MySQL连接句柄
         *
         * @return bool 初始化结果
         *         - true  初始化成功
         *         - false 初始化失败（数据容器无效或创建句柄失败）
         */
        bool Init();
        /**
         * @brief 关闭并释放MySQL连接资源
         *
         * 该函数用于释放内部管理的MySQL连接数据。若连接数据指针(m_pData)为空，则直接返回；
         * 否则显式调用析构函数进行资源回收。
         *
         * @note 该函数无参数和返回值
         */
        void Close();
        /**
         * @brief 连接到MySQL数据库服务器
         *
         * @param ip 数据库服务器的IP地址或主机名
         * @param user 数据库登录用户名
         * @param pwd 数据库登录密码
         * @param dbname 要连接的默认数据库名称
         * @param port 数据库服务器的端口号
         * @param flag 客户端连接标志位（用于设置连接选项）
         * @return bool 连接成功返回true，失败返回false
         */
        bool Connect(const char *ip, const char *user, const char *pwd, const char *dbname, unsigned short port = 3306, unsigned long flag = 0);
        /*
        mysql 参数设置，需要在connect之前调用
        */
        bool SetOption(LX_OPT option, const char *value);
        void PrintError();
        std::string GetErrorStr();
        /**
         * @brief 设置MySQL连接的读写超时时间
         * 
         * 该函数用于配置MySQL连接的读取和写入操作的超时时间。若连接未初始化或设置过程中发生错误，
         * 将返回对应的错误码。
         * 
         * @param read_timeout 读取操作的超时时间（单位：秒），0表示使用默认值
         * @param write_timeout 写入操作的超时时间（单位：秒），0表示使用默认值
         * @return int 执行结果状态码
         *   - 0  设置成功
         *   - -1 MySQL连接未初始化（m_pData->mysql为nullptr）
         *   - 1  设置读取超时失败（参考mysql_options返回值）
         *   - 2  设置写入超时失败（参考mysql_options返回值）
         */
        int SetTimeout(unsigned int read_timeout, unsigned int write_timeout);
        bool SetReconnect(bool isreconnect);
        int SetConnectTimeout(unsigned int connect_timeout_src);

        /**
         * @brief 获取结果集的下一行数据
         *
         * 遍历MySQL查询结果集，将游标移动到下一行数据，并更新相关字段信息。
         * 当结果集耗尽或出现错误时返回false，否则返回true。
         *
         * @return bool
         *         - true  成功获取下一行数据
         *         - false 结果集不存在或已到末尾
         */
        bool Next();
        // Exec会执行SQL语句
        // 并返根据type调用mysql_use_result或者mysql_store_result获取结果集
        /**
         * @brief 执行SQL语句并处理结果集
         * 
         * @param sql 需要执行的SQL语句字符串
         * @param type 执行类型枚举值，决定结果集获取方式：
         *             EXEC_TYPE_USE 使用流式结果集
         *             其他值使用存储式结果集
         * @return bool 执行成功且获取到结果集返回true，
         *              连接失效或执行失败返回false
         */
        bool Exec(std::string sql, EXEC_TYPE type = EXEC_TYPE::EXEC_TYPE_USE);
        /**
         * @brief 执行SQL语句并处理结果集
         * 
         * @param sql 需要执行的SQL语句字符串
         * @param type 执行类型枚举值，决定结果集获取方式：
         *             EXEC_TYPE_USE 使用流式结果集
         *             其他值使用存储式结果集
         * @return bool 执行成功且获取到结果集返回true，
         *              连接失效或执行失败返回false
         */
        bool Exec(const char *sql, size_t len = 0, EXEC_TYPE type = EXEC_TYPE::EXEC_TYPE_USE);
        // Query不会调用mysql_use_result或者mysql_store_result获取结果集
            /**
         * @param sql 要执行的SQL查询语句
         * @param len SQL查询语句的长度
         * @return bool 查询是否成功
         * 
         * 此函数首先检查传入的MySQL连接和SQL查询语句是否有效，如果无效则返回false。
         * 然后根据传入的长度参数选择合适的查询函数：如果长度小于等于0，则使用mysql_query函数；
         * 否则使用mysql_real_query函数。最后根据查询函数的返回值判断查询是否成功，并返回相应的布尔值。
         */
        bool Query(const char *sql, size_t len = 0);
        /**
         * @param sql 要执行的SQL查询语句
         * @param len SQL查询语句的长度
         * @return bool 查询是否成功
         * 
         * 此函数首先检查传入的MySQL连接和SQL查询语句是否有效，如果无效则返回false。
         * 然后根据传入的长度参数选择合适的查询函数：如果长度小于等于0，则使用mysql_query函数；
         * 否则使用mysql_real_query函数。最后根据查询函数的返回值判断查询是否成功，并返回相应的布尔值。
         */
        bool Query(std::string sql);
        // 获取结果集的列数
        unsigned long Columns();
        // Exec指定EXEC_TYPE_STORE或者时StoreResult才有效
        uint64_t Rows();
        bool StoreResult();
        bool UseResult();
        void FreeResult();
        // 返回当前行的第index数据
        std::string Value(int index);
        // 获取结果集的下一行数据（也就是会先调用Next）
        std::vector<LXData> FetchRow();
        // 生成插入SQL语句
        // 参数data是一个键值对集合，表示要插入的字段名和对应的值
        // 参数table是数据库表名
        // 返回值是生成的SQL语句字符串
        std::string GetInsertSql(const XDATA &data,const char *table);
        //
        template<class T>
        std::string GetInsertSql(const XDATA &data,T&&table){
            return GetInsertSql(data,table.c_str());
        }
        /**
         * @brief 向指定的MySQL表中插入数据
         *
         * 本函数根据提供的数据和表名生成并执行一条INSERT SQL语句
         * 如果操作成功，返回true；否则，返回false
         *
         * @param data 要插入的数据，类型为XDATA
         * @param table 表名的字符串指针
         * @return true 插入成功
         * @return false 插入失败
         */
        bool Insert(const XDATA &data,const char *table);
        /**
         * @brief 插入数据到指定表中
         * 
         * 该函数是一个模板函数，能够接受任意类型的data和table作为参数
         * 它的主要作用是将data插入到指定的table中
         * 
         * @param data 要插入的数据，可以是左值或右值
         * @param table 插入数据的目标表，可以是任何支持.c_str()方法的对象
         * @return bool 插入操作的成功与否，成功返回true，失败返回false
         */
        template<class DATA,class T>
        inline bool Insert(DATA&&data,T&&table){
            return Insert((const XDATA &)data,(const char *)table.c_str());
        }
        // 插入二进制数据到指定的MySQL表中
        /**
         * @param data 包含要插入数据的容器，类型为XDATA
         * @param table 要插入数据的表名
         * @return 成功插入返回true，否则返回false
         *
         * 该函数将二进制数据插入到指定的MySQL表中。它首先检查输入参数的有效性，
         * 然后构造插入SQL语句，并使用MySQL C API进行预处理、绑定参数和执行SQL语句。
         */
        bool InsertBinary1(const XDATA &data,const char *table, const char**err_str);
        bool InsertBinary(const XDATA &data,const char *table);
        template<class DATA,class T>
        inline bool InsertBinary(DATA&&data,T&& table){
            return InsertBinary((const XDATA &)data,(const char *)table.c_str());
        }

        std::string GetUpdateSql(const XDATA &data,const char*table,const char* where);
        template<class DATA,class T,class B>
        inline std::string GetUpdateSql(DATA&&data,T&& table,B&& where){
            return GetUpdateSql((const XDATA &)data,(const char *)table.c_str(),(const char *)where.c_str());
        }

        int Update(const XDATA &data,const std::string&table,const std::string &where);
        template<class DATA,class T,class B>
        bool Update(DATA&&data,T&&table,T&&where){
            return Update((const XDATA &)data,(const char *)table.c_str(),(const char *)where.c_str());
        }
        int UpdateBinary(const XDATA &data,const char*table,const char* where);
        //简易接口,返回select的数据结果，每次调用清理上一次的结果集
        XROWS GetResult(const char *sql);
        XROWS GetResult(const std::string &sql){
            return GetResult(sql.c_str());
        }
        XROWS GetResult(std::string &&sql){
            return GetResult(sql.c_str());
        }
    private:
        LXMysqlData *m_pData = nullptr;
    };
} // namespace LX
